/*
    Copyright (C) 2011  Jiabo <jiabo2011@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

static inline void gen_op_OP1_OP2(DisasContext *s1, int op, int ot)
{
	switch(op) {
	case OP_ADDL:
		arm_add_reg(T1, OP1, OP2);
		arm_str_data(ot, T0, T1);
		s1->cc_op = CC_OP_ADDB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ORL:
		arm_orr_reg(OP1, OP1, OP2);
		arm_str_data(ot, T0, OP1);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ADCL:
	case OP_SBBL:
		printf("%s: unsupported cmd adc or sbb\n", __func__);
		exit(0);
	case OP_ANDL:
		arm_and_reg(OP1, OP1, OP2);
		arm_str_data(ot, T0, OP1);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SUBL:
		arm_sub_reg(T1, OP1, OP2);
		arm_str_data(ot, T0, T1);
		s1->cc_op = CC_OP_SUBB + ot;
		s1->opc_valid = 1;
		break;
	case OP_XORL:
		arm_eor_reg(OP1, OP1, OP2);
		arm_str_data(ot, T0, OP1);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_CMPL:
		s1->cc_op = CC_OP_CMPB + ot;
		s1->opc_valid = 1;
		break;
	}
}

static inline void gen_op_OP1_REG(DisasContext *s1, int op, int ot, uint32_t reg)
{
	load_x86reg(ot, OP2, reg);
	gen_op_OP1_OP2(s1, op, ot);
}


static inline void gen_op_OP1_IMM(DisasContext *s1, int op, int ot, uint32_t imm32)
{
	gen_mov_reg_imm(OP2, imm32);
	gen_op_OP1_OP2(s1, op, ot);
}

static inline void gen_op_REG_OP2(DisasContext *s1, int op, int ot, uint32_t reg)
{
	load_x86reg(ot, OP1, reg);
	switch(op) {
	case OP_ADDL:
		arm_add_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_ADDB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ORL:
		arm_orr_reg(OP1, OP1, OP2);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ADCL:
		/* eflags should calculated first */
		arm_add_reg(T1, OP1, OP2);
		arm_tst_reg_imm(R0, CC_C, 0);
		arm_add_reg_imm_cond(COND_NE, T1, 1, 0);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_ADCB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SBBL:
		/* eflags should calculated first */
		arm_sub_reg(T1, OP1, OP2);
		arm_tst_reg_imm(R0, CC_C, 0);
		arm_sub_reg_imm_cond(COND_NE, T1, 1, 0);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_SBBB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ANDL:
		arm_and_reg(OP1, OP1, OP2);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SUBL:
		arm_sub_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_SUBB + ot;
		s1->opc_valid = 1;
		break;
	case OP_XORL:
		arm_eor_reg(OP1, OP1, OP2);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_CMPL:
		s1->cc_op = CC_OP_CMPB + ot;
		s1->opc_valid = 1;
		break;
	}
}

static inline void gen_op_REG_REG(DisasContext *s1, int op, int ot, uint32_t reg1, uint32_t reg2)
{
	switch(op) {
	case OP_ADDL:
		load_x86reg(ot, OP1, reg2);
		load_x86reg(ot, OP2, reg1);
		arm_add_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg2);
		s1->cc_op = CC_OP_ADDB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ORL:
		load_x86reg(ot, OP1, reg2);
		if (reg1 != reg2) {
			load_x86reg(ot, T0, reg1);
			arm_orr_reg(OP1, OP1, T0);
			store_x86reg(ot, OP1, reg2);
		}
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ADCL:
	case OP_SBBL:
		printf("%s: unsupported cmd adc or sbb\n", __func__);
		exit(0);
	case OP_ANDL:
		load_x86reg(ot, OP1, reg2);
		load_x86reg(ot, T0, reg1);
		arm_and_reg(OP1, OP1, T0);
		store_x86reg(ot, OP1, reg2);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SUBL:
		load_x86reg(ot, OP1, reg2);
		load_x86reg(ot, OP2, reg1);
		arm_sub_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg2);
		s1->cc_op = CC_OP_SUBB + ot;
		s1->opc_valid = 1;
		break;
	case OP_XORL:
		if (reg1 == reg2) {
			gen_mov_reg_imm(OP1, 0);
		} else {
			load_x86reg(ot, OP1, reg2);
			load_x86reg(ot, T0, reg1);
			arm_eor_reg(OP1, OP1, T0);
		}
		store_x86reg(ot, OP1, reg2);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_CMPL:
		load_x86reg(ot, OP1, reg2);
		load_x86reg(ot, OP2, reg1);
		s1->cc_op = CC_OP_CMPB + ot;
		s1->opc_valid = 1;
		break;
	}
}

static inline void gen_op_REG_IMM(DisasContext *s1, int op, int ot, uint32_t reg, uint32_t imm32)
{
	switch(op) {
	case OP_ADDL:
		load_x86reg(ot, OP1, reg);
		gen_mov_reg_imm(OP2, imm32);
		arm_add_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_ADDB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ORL:
		load_x86reg(ot, OP1, reg);
		gen_orr_reg_imm(OP1, imm32, T1);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_ADCL:
		gen_x86_flags(s1);

		load_x86reg(ot, OP1, reg);
		gen_mov_reg_imm(OP2, imm32);
		arm_add_reg(T1, OP1, OP2);
		arm_tst_reg_imm(R0, CC_C, 0);
		arm_add_reg_imm_cond(COND_NE, T1, 1, 0);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_ADCB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SBBL:
		printf("%s: unsupported cmd sbb\n", __func__);
		exit(0);
	case OP_ANDL:
		load_x86reg(ot, OP1, reg);
		gen_and_reg_imm(OP1, imm32, T1);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_SUBL:
		load_x86reg(ot, OP1, reg);
		gen_mov_reg_imm(OP2, imm32);
		arm_sub_reg(T1, OP1, OP2);
		store_x86reg(ot, T1, reg);
		s1->cc_op = CC_OP_SUBB + ot;
		s1->opc_valid = 1;
		break;
	case OP_XORL:
		load_x86reg(ot, OP1, reg);
		gen_eor_reg_imm(OP1, imm32, T1);
		store_x86reg(ot, OP1, reg);
		s1->cc_op = CC_OP_LOGICB + ot;
		s1->opc_valid = 1;
		break;
	case OP_CMPL:
		load_x86reg(ot, OP1, reg);
		gen_mov_reg_imm(OP2, imm32);
		s1->cc_op = CC_OP_CMPB + ot;
		s1->opc_valid = 1;
		break;
	}
}

